﻿#ifndef TIMER_H_
#define TIMER_H_

#include <functional>
#include <future>
#include <chrono>
#include <thread>
#include <atomic>
#include <memory>
#include <mutex>
#include <condition_variable>

namespace as
{
	class Timer
	{
	public:
		Timer();
		Timer(const Timer &t);
		~Timer();

		struct TimerEvent
		{
			int64_t id_;
			int interval_ = 0;
			std::time_t lastTimeout_ = 0;
			std::function<void()> task_;
		};
		void Tick();
		void Stop();
		bool IsRun() const { return isRun_; }

		// t.SyncWait(1000, EchoFunc, "hello world!");
		template<typename callable, class... arguments>
		void SyncWait(int after, callable &&f, arguments&&... args)
		{
			if (isRun_ == false)
				return;

			auto task = [after, f = std::forward<callable>(f), arg = std::make_tuple(std::forward<arguments>(args)...)]()
				{
					std::this_thread::sleep_for(std::chrono::milliseconds(after));
					std::apply(f, arg);
				};
			task();
		}

		// t.AsyncWait(1000, EchoFunc, "hello c++11!");
		template<typename Fun, class... Args>
		void AsyncWait(int after, Fun &&f, Args&&... args)
		{
			using ReturnType = std::invoke_result_t<Fun, Args...>;

			if (isRun_ == false)
				return;

			auto time_now = std::chrono::system_clock::now();
			auto duration_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_now.time_since_epoch());


			TimerEvent eve;
			std::hash<std::thread::id> hasher;
			uint64_t threadId = static_cast<uint64_t>(hasher(std::this_thread::get_id()));
			eve.id_ = (threadId << 32) & ((duration_in_ms.count() / 1000) & UINT_MAX);

			auto task = std::make_shared<std::packaged_task<ReturnType()>>(std::bind(std::forward<Fun>(f), std::forward<Args>(args)...));
			eve.task_ = [task]() { (*task)(); };
			eve.interval_ = after;
			eve.lastTimeout_ = duration_in_ms.count();

			{
				std::lock_guard<std::mutex> l(mutex_);
				tasks_.emplace_back(eve);
			}
			expired_cond_.notify_one();
		}

	private:
		std::atomic<bool> isRun_;
		std::mutex mutex_;
		std::condition_variable expired_cond_;
		std::vector<TimerEvent> tasks_;
	};

	class TimerThreadPool
	{
	public:
		TimerThreadPool()
		{
			timers_.resize(std::thread::hardware_concurrency() * 2);
		}

		~TimerThreadPool() {}
	protected:
		void Run()
		{
			while (isRun_)
			{
				for (auto &thr : timers_)
				{
					for (auto &timer : thr)
					{
						timer->Tick();
					}
				}
			}

			//退出前执行完所有到期的定时器
			for (auto &thr : timers_)
			{
				for (auto &timer : thr)
				{
					timer->Tick();
				}
			}
		}

	public:
		void Start(int threadNum)
		{
			isRun_ = true;
			thread_.reserve(threadNum);
			for (int i = 0; i < threadNum; ++i)
			{
				thread_.emplace_back(std::thread(std::bind(&TimerThreadPool::Run, this)));
			}
		}

		void Stop()
		{
			isRun_ = false;
			for (auto &thr : timers_)
			{
				for (auto &timer : thr)
				{
					timer->Stop();
				}
			}

			for (auto &thr : thread_)
			{
				thr.join();
			}
			thread_.clear();
		}

		void PushTimer(Timer *timer)
		{
			if (isRun_)
				return;
			timers_[pushThreadIndex_++].emplace_back(timer);
			pushThreadIndex_ = pushThreadIndex_ % timers_.size();
		}



	private:
		std::atomic_bool isRun_ = false;
		unsigned char pushThreadIndex_ = 0;
		std::vector<std::thread> thread_;
		std::vector<std::vector<Timer *>> timers_;
	};
}

#endif
